#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <glib.h>
#include <libusb-1.0/libusb.h>

#include "dict.h"
#include "date.h"
#include "encrypt.h"
#include "kylin-ukey.h"
#include "kylin-vikey.h"
#include "kylin-ftkey.h"
#include "kylin-lmkey.h"
#include "kylin-fykey.h"
#include "kylin-verify.h"
#include "kylin-activation.h"

#include "ukey-macro.h"

#define SALT_LENGTH 2
#define ENCRYPTED_NUMBER_LENGTH 20
#define DEFAULT_DATE "20000101"

#ifndef ENCRYPTED_DATE_LENGTH
#define ENCRYPTED_DATE_LENGTH 4
#endif

#define BUF_SIZE 1024

#define VIKEY 1
#define VPIDLIST "/usr/share/kylin-activation/activation_ukey_conf.ini"

static struct dict_set ukey_dict =
{
	.year_dict = {'Z', 'U', '7', '2', 'D', 'Q', 'K', 'W', '9', '4', 'F', 'A', 'M', 'Y', 'T'},
	.yday_dict = {'Y', '9', '2', 'B', 'L', 'V', '6', 'F', 'Q', 'Z', 'S', '3', 'C', 'M', 'W', '7', 'G', 'R', 'J', 'T'},
	.dict = {'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', '9', '8', '7', '6', '5', '4', '3', '2', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', 'R', 'Q', 'P', 'N', 'M', 'L', 'K', 'J'},
	.dict_type = UKEY_DICT
};

// 发现的 vikey 加密锁数量
#ifdef ENABLE_VIKEY
static int gVikeyCount = 0;
#endif // ENABLE_VIKEY

// 发现的 feitian key 加密锁数量
#ifdef ENABLE_FTKEY
static int gFtkeyCount = 0;
#endif // ENABLE_FTKEY
/*
 * ukey_type 的值
 */
enum UKEY_TYPE {
	UKEY_TYPE_UNKNOWN 	= 0,
	UKEY_TYPE_VIKEY   	= 1,
	UKEY_TYPE_FT_ROCKEYARM	= 2,
	UKEY_TYPE_LMKEY	= 3,
	UKEY_TYPE_FYKEY	= 4
};

static int ukey_type = -1;

#ifdef ENABLE_VIKEY
extern int vikey_load_library();
#endif // ENABLE_VIKEY

#ifdef ENABLE_FTKEY
extern int ftkey_load_library();
#endif // ENABLE_FTKEY

#ifdef ENABLE_LMKEY
extern int lmkey_load_library();
#endif // ENABLE_LMKEY

#ifdef ENABLE_FYKEY
extern int fykey_load_library();
#endif // ENABLE_FYKEY

/*
 * _ukey_encrypted_number_generate, generate encrypted code for ukey
 * return encrypted code if succeed, or NULL if not
 */
static char* _ukey_encrypted_number_generate(const char* register_code, const char* ukey_serial, const char* date, struct dict_set *dicts)
{
	char salt[SALT_LENGTH + 1];
	char* raw = NULL;
	char* encrypted_date = NULL;
	size_t l_s = 0;
	size_t l_ed = 0;

	if(!register_code_is_valid(register_code))
	{
		//fprintf(stderr, "注册码无效\n");
		return NULL;
	}

	salt[0] = register_code[ENCRYPTED_NUMBER_LENGTH - 1];
	salt[1] = register_code[0];
	salt[SALT_LENGTH] = '\0';

	raw = encrypted_number_generate_with_dict(register_code, ukey_serial, salt, dicts);
	if(!raw)
		return NULL;

	encrypted_date = date_encrypt_with_dict(date, dicts);
	if(!encrypted_date)
	{
		free(raw);
		return NULL;
	}

	l_s = strlen(salt);
	l_ed = strlen(encrypted_date);

	encrypted_date = adjust_encrypted_date(encrypted_date, raw, dicts);

	memcpy(&raw[ENCRYPTED_NUMBER_LENGTH - l_ed - l_s], encrypted_date, l_ed);

	g_free(encrypted_date);

	return raw;
}

/*
 * ukey_encrypted_number_generate, generate encrypted code for ukey
 * return encrypted code if succeed, or NULL if not
 */
static char* ukey_encrypted_number_generate(const char* register_code, const char* ukey_serial, const char* date, struct dict_set *dicts)
{
	return _ukey_encrypted_number_generate(register_code, ukey_serial, date, &ukey_dict);
}

/*
 * encrypted_number_generate_ukey, generate encrypted code for ukey
 * return encrypted code if succeed, or NULL if not
 */
char* encrypted_number_generate_ukey(const char* register_code, const char* ukey_serial, const char* date)
{
	return ukey_encrypted_number_generate(register_code, ukey_serial, date, &ukey_dict);
}

/*
 * _ukey_activate_number_validate_with_dict, validate ukey activation code
 * return 1 if valid, or 0 if not
 */
static int _ukey_activate_number_validate_with_dict(const char* register_code, const char* ukey_serial, const char* activate_code, struct dict_set* dicts)
{
	char* raw = NULL;
	if(!register_code || !ukey_serial || !activate_code)
		return 0;

	raw = ukey_encrypted_number_generate(register_code, ukey_serial, DEFAULT_DATE, dicts);
	if(!raw)
		return 0;

	return activate_number_compare(raw, activate_code);
}

/*
 * ukey_activate_number_validate_with_dict, validate ukey activation code
 * return 1 if valid, or 0 if not
 */
static int ukey_activate_number_validate_with_dict(const char* register_code, const char* ukey_serial, const char* activate_code, struct dict_set* dicts)
{

	if(!register_code_is_valid(register_code))
		return 0;
	if(!activate_code_is_valid(activate_code, dicts))
		return 0;

	return _ukey_activate_number_validate_with_dict(register_code, ukey_serial, activate_code, dicts);
}

/*
 * activate_number_validate_ukey, validate ukey activation code
 * return 1 if valid, or 0 if not
 */
int activate_number_validate_ukey(const char* register_code, const char* ukey_serial, const char* activate_code)
{
	return ukey_activate_number_validate_with_dict(register_code, ukey_serial, activate_code, &ukey_dict);
}

/*
 * _ukey_activation_expire_date_with_dict, get expire date in form of struct tm from ukey activation code
 * return valid expire date pointer if succeed, or NULL if not
 */
static struct tm* _ukey_activation_expire_date_with_dict(const char* register_code, const char* ukey_serial, const char* activate_code, struct dict_set* dicts)
{
	struct tm* expire_date_tm = NULL;
	char encrypted_date[ENCRYPTED_DATE_LENGTH + 1];

	if(!ukey_activate_number_validate_with_dict(register_code, ukey_serial, activate_code, dicts))
		return NULL;

	memset(encrypted_date, 0, ENCRYPTED_DATE_LENGTH + 1);

	memcpy(encrypted_date, &activate_code[ENCRYPTED_NUMBER_LENGTH - ENCRYPTED_DATE_LENGTH - SALT_LENGTH], ENCRYPTED_DATE_LENGTH);

	if(-1 == restore_encrypted_date(encrypted_date, activate_code, dicts))
		return NULL;

	expire_date_tm = date_decrypt_with_dict(encrypted_date, dicts);

	return expire_date_tm;
}

/*
 * ukey_activation_expire_date_with_dict, get expire date in form of struct tm from ukey activation code
 * return valid expire date pointer if succeed, or NULL if not
 */
static struct tm* ukey_activation_expire_date_with_dict(const char* register_code, const char* ukey_serial, const char* activate_code, struct dict_set* dicts)
{
	return _ukey_activation_expire_date_with_dict(register_code, ukey_serial, activate_code, &ukey_dict);
}

/*
 * activation_expire_date_ukey, get expire date in form of struct tm from ukey activation code
 * return valid expire date pointer if succeed, or NULL if not
 */
struct tm* activation_expire_date_ukey(const char* register_code, const char* ukey_serial, const char* activate_code)
{
	return ukey_activation_expire_date_with_dict(register_code, ukey_serial, activate_code, &ukey_dict);
}


/*
 * ukey_activate_system, activate system with ukey
 * return NO_ERROR if succeed, or error number if not
 */
int ukey_activate_system(const char* license_serial, const char* kyinfo_ukey, const char* expire_date)
{
    int ret = NO_UKEY_FOUND;
	int iUkeyType = -1;

    ret = ukey_usb_bus_find(&iUkeyType);
    if(ret != NO_ERROR) {
        return ret;
    }

	ukey_type = iUkeyType;

	ret = ukey_device_load(iUkeyType);
	if(ret != NO_ERROR) {
        return ret;
    }

#ifdef ENABLE_VIKEY
    if(ukey_type == UKEY_TYPE_VIKEY) {
        return vikey_activate_system(license_serial, kyinfo_ukey, expire_date, encrypted_number_generate_ukey);
    }
#endif // ENABLE_VIKEY

#ifdef ENABLE_FTKEY
    if (ukey_type == UKEY_TYPE_FT_ROCKEYARM) {
        return ftkey_activate_system(license_serial, kyinfo_ukey, expire_date, encrypted_number_generate_ukey);
    }
#endif // ENABLE_FTKEY

#ifdef ENABLE_LMKEY
    if (ukey_type == UKEY_TYPE_LMKEY) {
        return lmkey_activate_system(license_serial, kyinfo_ukey, expire_date, encrypted_number_generate_ukey);
    }
#endif // ENABLE_LMKEY

#ifdef ENABLE_FYKEY
    if (ukey_type == UKEY_TYPE_FYKEY) {
        return fykey_activate_system(license_serial, kyinfo_ukey, expire_date, encrypted_number_generate_ukey);
    }
#endif // ENABLE_FYKEY



    if (ukey_type == UKEY_TYPE_UNKNOWN) {
        return NO_UKEY_FOUND;
    }

	return ret;
}


/*
 * ukey_get_hid, get hid string of ukey
 * return hid string if succeed, or NULL if not
 */
char* ukey_get_hid()
{
#ifdef ENABLE_VIKEY
	if( ukey_type == UKEY_TYPE_VIKEY ) {
		return vikey_get_hid();
	}
#endif // ENABLE_VIKEY

#ifdef ENABLE_FTKEY
	if(ukey_type == UKEY_TYPE_FT_ROCKEYARM) {
		return ftkey_get_hid();
	}
#endif // ENABLE_FTKEY

#ifdef ENABLE_LMKEY
	if(ukey_type == UKEY_TYPE_LMKEY) {
		return lmkey_get_hid();
	}
#endif // ENABLE_LMKEY

#ifdef ENABLE_FYKEY
	if(ukey_type == UKEY_TYPE_FYKEY) {
		return fykey_get_hid();
	}
#endif // ENABLE_FYKEY


	return NULL;
}

/*
 * ukey_dump, dump ukey information to file f
 * encrypt: whether encryption is needed or not
 * display_all: whether display all information or not
 * return NO_ERROR if succeed, or error number if not
 */
int ukey_dump(FILE* f, int encrypt, int display_all)
{
    int ret = NO_UKEY_FOUND;
	int iUkeyType = -1;

    ret = ukey_usb_bus_find(&iUkeyType);
    if(ret != NO_ERROR) {
        return ret;
    }

	ukey_type = iUkeyType;

	ret = ukey_device_load(iUkeyType);
	if(ret != NO_ERROR) {
        return ret;
    }

#ifdef ENABLE_VIKEY
	if( ukey_type == UKEY_TYPE_VIKEY ) {
		return vikey_dump(f, encrypt, display_all);
	}
#endif // ENABLE_VIKEY

#ifdef ENABLE_FTKEY
	if( ukey_type == UKEY_TYPE_FT_ROCKEYARM) {
		return ftkey_dump(f, encrypt, display_all);
	}
#endif // ENABLE_FTKEY

#ifdef ENABLE_LMKEY
	if( ukey_type == UKEY_TYPE_LMKEY) {
		return lmkey_dump(f, encrypt, display_all);
	}
#endif // ENABLE_LMKEY

#ifdef ENABLE_FYKEY
	if( ukey_type == UKEY_TYPE_FYKEY) {
		return fykey_dump(f, encrypt, display_all);
	}
#endif // ENABLE_FYKEY


	return NO_UKEY_FOUND;
}

/*
 * find ukey
 * return NO_ERROR if vikey is inserted, or error number if not
 */
int ukey_find()
{
#ifdef ENABLE_FTKEY
	unsigned int count = 0;
#endif // ENABLE_FTKEY

    int ret = NO_UKEY_FOUND;
    (void)ret;

#ifdef ENABLE_VIKEY
    ret = vikey_load_library();
    if(ret != NO_ERROR) {
        return ret;
    }
#endif // ENABLE_VIKEY

#ifdef ENABLE_FTKEY
    ret = ftkey_load_library();
    if(ret != NO_ERROR) {
        return ret;
    }
#endif // ENABLE_FTKEY

#ifdef ENABLE_LMKEY
    ret = lmkey_load_library();
    if(ret != NO_ERROR) {
        return ret;
    }
#endif // ENABLE_LMKEY

#ifdef ENABLE_FYKEY
    ret = fykey_load_library();
    if(ret != NO_ERROR) {
        return ret;
    }
#endif // ENABLE_FYKEY


#ifdef ENABLE_VIKEY
	ret = vikey_find(&count);
	gVikeyCount = count;
	if(ret == NO_ERROR) {
		// vikey
		ukey_type = UKEY_TYPE_VIKEY;
		return ret;
	}
#endif // ENABLE_VIKEY

#ifdef ENABLE_FTKEY
	count = 0;
	ret = ftkey_find(&count);
	gFtkeyCount = count;
	if(ret == NO_ERROR) {
		// feitian RockeyARM
		ukey_type = UKEY_TYPE_FT_ROCKEYARM;
		return ret;
	}
#endif // ENABLE_FTKEY

#ifdef ENABLE_LMKEY
	count = 0;
	ret = lmkey_find(&count);
	gFtkeyCount = count;
	if(ret == NO_ERROR) {
		// feitian RockeyARM
		ukey_type = UKEY_TYPE_LMKEY;
		return ret;
	}
#endif // ENABLE_LMKEY

#ifdef ENABLE_FYKEY
	count = 0;
	ret = fykey_find(&count);
	gFtkeyCount = count;
	if(ret == NO_ERROR) {
		ukey_type = UKEY_TYPE_FYKEY;
		return ret;
	}
#endif // ENABLE_FYKEY

	// 未知的 ukey
	ukey_type = UKEY_TYPE_UNKNOWN;
	return NO_UKEY_FOUND;
}

/*
 * find ukey by usb bus
 * return NO_ERROR if vikey is inserted, or error number if not
 */
int ukey_usb_bus_find(int* icUkeyType)
{
	int iRet = NO_UKEY_FOUND;
	int iUkeyType = UKEY_TYPE_UNKNOWN;
	GKeyFile *gKeyFile = NULL;
	char* sVidValue = NULL,* sPidValue = NULL;
	int iVidValue = -1, iPidValue = -1;
	libusb_device_handle* hDevHandle;
    
    // 初始化 libusb 库
    iRet = libusb_init(NULL);
    if (iRet < 0) {
        fprintf(stderr, "Failed to initialize libusb: %s\n", libusb_error_name(iRet));
		iRet = NO_UKEY_FOUND;
        goto ukey_usb_bus_find_out;
    }

	gKeyFile = key_file_load_from_file(VPIDLIST);
	if (gKeyFile) {
		// 查找飞天 USB 设备
		sVidValue = key_file_get_value(gKeyFile, "Key_feitian", "vid");
		sPidValue = key_file_get_value(gKeyFile, "Key_feitian", "pid");
		iVidValue = strtol(sVidValue, NULL, 16);
		iPidValue = strtol(sPidValue, NULL, 16);

		hDevHandle = libusb_open_device_with_vid_pid(NULL, iVidValue, iPidValue);
		if (hDevHandle != NULL) {
			fprintf(stdout, "Found ftkey device: %04x:%04x\n",iVidValue,iPidValue);
			iUkeyType = UKEY_TYPE_FT_ROCKEYARM;
			*icUkeyType = iUkeyType;
			iRet = NO_ERROR;
			goto ukey_usb_bus_find_out;
		}

		// 查找龙脉 USB 设备
		sVidValue = key_file_get_value(gKeyFile, "Key_longmai", "vid");
		sPidValue = key_file_get_value(gKeyFile, "Key_longmai", "pid");
		iVidValue = strtol(sVidValue, NULL, 16);
		iPidValue = strtol(sPidValue, NULL, 16);

		hDevHandle = libusb_open_device_with_vid_pid(NULL, iVidValue, iPidValue);
		if (hDevHandle != NULL) {
			fprintf(stdout, "Found longmai device: %04x:%04x\n",iVidValue,iPidValue);
			iUkeyType = UKEY_TYPE_LMKEY;
			*icUkeyType = iUkeyType;
			iRet = NO_ERROR;
			goto ukey_usb_bus_find_out;
		}
		
		// 查找方圆 USB 设备
		sVidValue = key_file_get_value(gKeyFile, "Key_fangyuan", "vid");
		sPidValue = key_file_get_value(gKeyFile, "Key_fangyuan", "pid");
		iVidValue = strtol(sVidValue, NULL, 16);
		iPidValue = strtol(sPidValue, NULL, 16);

		hDevHandle = libusb_open_device_with_vid_pid(NULL, iVidValue, iPidValue);
		if (hDevHandle != NULL) {
			fprintf(stdout, "Found fangyuan device: %04x:%04x\n",iVidValue,iPidValue);
			iUkeyType = UKEY_TYPE_FYKEY;
			*icUkeyType = iUkeyType;
			iRet = NO_ERROR;
			goto ukey_usb_bus_find_out;
		}

	} else {
		iRet = NO_UKEY_FOUND;
        goto ukey_usb_bus_find_out;
	}
    
	iRet = NO_UKEY_FOUND;

ukey_usb_bus_find_out:
	// 关闭设备句柄并退出 libusb 库	
    libusb_exit(NULL);
	libusb_close(hDevHandle);

	if (gKeyFile) {
		g_key_file_free(gKeyFile);
		gKeyFile = NULL;
	}

    return iRet;
}

/*
 * return NO_ERROR if vikey is inserted, or error number if not
 */
int ukey_device_load(int icUkeyType)
{
	int iRet = -1;
	unsigned int count = 0;

	if( icUkeyType == UKEY_TYPE_VIKEY) {
#ifdef ENABLE_VIKEY
		iRet = vikey_load_library();
		if(iRet != NO_ERROR)
			goto ukey_device_load_out;
		
		iRet = vikey_find(&count);
		if(iRet == NO_ERROR) // vikey
			goto ukey_device_load_out;
#endif
	} else if ( icUkeyType == UKEY_TYPE_FT_ROCKEYARM) {
#ifdef ENABLE_FTKEY
		iRet = ftkey_load_library();
		if(iRet != NO_ERROR)
			goto ukey_device_load_out;

		iRet = ftkey_find(&count);
		if(iRet == NO_ERROR) // feitian RockeyARM
			goto ukey_device_load_out;
#endif
	} else if ( icUkeyType == UKEY_TYPE_LMKEY) {
#ifdef ENABLE_LMKEY
		iRet = lmkey_load_library();
		if(iRet != NO_ERROR)
			goto ukey_device_load_out;

		iRet = lmkey_find(&count);
		if(iRet == NO_ERROR) // longmai key
			goto ukey_device_load_out;
#endif
	} else if ( icUkeyType == UKEY_TYPE_FYKEY) {
#ifdef ENABLE_FYKEY
		iRet = fykey_load_library();
		if(iRet != NO_ERROR)
			goto ukey_device_load_out;
		
		iRet = fykey_find(&count);
		if(iRet == NO_ERROR) // fangyuan key
			goto ukey_device_load_out;
#endif
	} else { // unknown ukey
		ukey_type = UKEY_TYPE_UNKNOWN;
	}

ukey_device_load_out:
	return iRet;
}

bool _product_type_check(const char* license_serial, const char* ukey_serial)
{
	int l_length;
	int u_length;
	char l_type[2] = {0};
	char u_type[2] = {0};

	if(!license_serial || !ukey_serial)
		return false;

	l_length = strlen(license_serial);
	u_length = strlen(ukey_serial);

	if((l_length < BATCH_SERIAL_LEN) || (u_length < BATCH_SERIAL_LEN))
		return false;

	memcpy(l_type, &license_serial[l_length - 3], sizeof(l_type));
	memcpy(u_type, &ukey_serial[u_length - 3], sizeof(u_type));

	if(strncmp(l_type, u_type, sizeof(l_type)) == 0)
	{
		return true;
	}

	return false;
}

#if 0
int main(int argc, char** argv)
{
	char* register_code = NULL;
	char* activate_code = NULL;
	int ret = -1;
	struct tm* terminal = NULL;

	if(argc != 4)
		exit(1);

	register_code = argv[1];

	activate_code = ukey_encrypted_number_generate(register_code, argv[2], argv[3], &ukey_dict);

	if(!activate_code)
		exit(2);

	printf("%s\n", activate_code);

	ret = ukey_activate_number_validate_with_dict(register_code, argv[2], activate_code, &ukey_dict);
	if(ret == 0)
	{
		printf("invalid activation code\n");
		exit(3);
	}
	else
		printf("valid activation code\n");


	terminal = ukey_activation_expire_date_with_dict(register_code, argv[2], activate_code, &ukey_dict);
	if(!terminal)
		exit(4);

	printf("%-4d year\n", terminal->tm_year + 1900);
	printf("%-4d mon\n", terminal->tm_mon + 1);
	printf("%-4d mday\n", terminal->tm_mday);
	printf("%-4d yday\n", terminal->tm_yday);
	printf("%-4d hour\n", terminal->tm_hour);
	printf("%-4d min\n", terminal->tm_min);
	printf("%-4d sec\n", terminal->tm_sec);
	printf("%-4d wday\n", terminal->tm_wday);
	printf("%-4d isdst\n", terminal->tm_isdst);

	printf("%-4d-%02d-%02d\n", terminal->tm_year + 1900, terminal->tm_mon + 1, terminal->tm_mday);

	return 0;

}

#endif
